`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2017/11/02 14:52:16
// Design Name: 
// Module Name: alu
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////

`include "alucontrol.vh"

module alu(
	input wire clk,rst,stall,flush,
	input wire[31:0] a,b,
	input wire[4:0] op, // alucontrol
	input wire[4:0] saE,
	output reg[63:0] y,
	output wire busy,
	output wire overflow,
	output wire zero
    );
    wire[31:0] subresult;
	assign subresult = a + (~b + 1);

	wire HIwe,LOwe;
	wire[31:0] HIout,LOout;
	flopenr #(32) HI(clk,rst,HIwe,y[63:32],HIout);
	flopenr #(32) LO(clk,rst,LOwe,y[31:0],LOout);

	wire[63:0] div_out;
	wire div_ready;
	div div(clk,rst,op == `ALUCONTROL_DIV,a,b,stall & (op == `ALUCONTROL_DIV || op == `ALUCONTROL_DIVU),flush,div_out,div_ready);
	assign HIwe = ~stall & ~flush & (op == `ALUCONTROL_MTHI || op == `ALUCONTROL_MULT || op == `ALUCONTROL_MULTU || div_ready & (op == `ALUCONTROL_DIV | op == `ALUCONTROL_DIVU));
	assign LOwe = ~stall & ~flush & (op == `ALUCONTROL_MTLO || op == `ALUCONTROL_MULT || op == `ALUCONTROL_MULTU || div_ready & (op == `ALUCONTROL_DIV | op == `ALUCONTROL_DIVU));
	assign busy = (op == `ALUCONTROL_DIV | op == `ALUCONTROL_DIVU) & ~div_ready;
	
	always @(*) begin
		case (op)
			`ALUCONTROL_SLT:	y <= (a[31] == 1 && b[31] == 0) ? 1 : (a[31] == 0 && b[31] == 1) ? 0 : subresult[31];
			`ALUCONTROL_SLTU: y <= a < b;
			`ALUCONTROL_XOR:	y <= a ^ b;
			`ALUCONTROL_NOR:	y <= ~(a | b);
			`ALUCONTROL_LUI:	y <= {b[15:0], 16'b0};
			`ALUCONTROL_AND: y <= a & b;
			`ALUCONTROL_OR: y <= a | b;
			`ALUCONTROL_ADD, `ALUCONTROL_ADDU: y <= a + b;
			`ALUCONTROL_SUB, `ALUCONTROL_SUBU: y <= subresult;
			//leftmove if overflow then %32
			//no think negnative number
			`ALUCONTROL_SLLV: y <= b<<a[4:0];
			`ALUCONTROL_SLL: y<= b<< saE;
			//
			`ALUCONTROL_SRAV: y<= $signed (b) >>> a[4:0];
			`ALUCONTROL_SRA: y<= $signed (b)>>> saE;
			`ALUCONTROL_SRLV: y<= b >> a[4:0];
			`ALUCONTROL_SRL: y<= b >> saE;
			

			// below is another method to implement SLT
			//`ALUCONTROL_SLT: y <= ((a[31] && !b[31]) || (!a[31] && !b[31] && subresult[31]) ||  (a[31] && b[31] && subresult[31]));

			`ALUCONTROL_MFHI: y <= HIout;
			`ALUCONTROL_MFLO: y <= LOout;
			`ALUCONTROL_MTHI: y <= {a, 32'b0};
			`ALUCONTROL_MTLO: y <= {32'b0, a};

			`ALUCONTROL_MULT: y <= $signed(a) * $signed(b);
			`ALUCONTROL_MULTU: y <= {32'b0, a} * {32'b0, b};
			`ALUCONTROL_DIV, `ALUCONTROL_DIVU: y <= div_out;
			 `ALUCONTROL_MTC0 : y <= b;
			 `ALUCONTROL_MFC0 : y <= a;

			default : y <= 32'b0;
		endcase	
	end

	assign zero = (y == 32'b0);
    
    assign overflow = (op == `ALUCONTROL_ADD)? (y[31] && !a[31] && !b[31]) || (!y[31] && a[31] && b[31]):
                  (op == `ALUCONTROL_SUB)? ((a[31]&!b[31])&!y[31]) || ((!a[31]&b[31])&y[31]):
                  1'b0; 
    
    /*
	always @(*) begin
		case (op[2:1])
			2'b01:overflow <= a[31] & b[31] & ~s[31] |
							~a[31] & ~b[31] & s[31];
			2'b11:overflow <= ~a[31] & b[31] & s[31] |
							a[31] & ~b[31] & ~s[31];
			default : overflow <= 1'b0;
		endcase	
	end
	*/
endmodule
